package com.hzgj.bcl.soa.rpc.server;

import com.esotericsoftware.reflectasm.MethodAccess;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;

/**
 * Created by Administrator on 2016/8/8.
 */
public class MethodReflectContainer {

    private static final Logger logger = LoggerFactory.getLogger(MethodReflectContainer.class);
    protected HashMap<MethodKey, MethodExecutor> executors = new HashMap<>();

    public void registerMethods(Class<?> clazz, Object instance, String name) {
        logger.info("register service: " + name);

        MethodAccess access = MethodAccess.get(clazz);
        Method[] methods = clazz.getMethods();

        Map<String, Long> methodCount = Arrays.asList(methods).stream().map(method -> method.getName()).collect(Collectors.groupingBy(method -> method, Collectors.counting()));

        methodCount.entrySet().stream().forEach(entry -> {
            if (entry.getValue() > 1) {
                throw new RuntimeException("not support method overloading of " + entry.getKey() + " in " + name);
            }
        });

        for (Method m : methods) {
            if (m.getDeclaringClass() == Object.class) {
                continue;
            }

            int index;
            try {
                index = access.getIndex(m.getName());
            } catch (IllegalArgumentException e) {
                logger.warn("find method index failed: {}", e);
                continue;
            }

            MethodExecutor mi = new MethodExecutor(instance, access, index);
            executors.put(buildKey(name, m.getName()), mi);
            logger.trace("register method: {}.{}", name, m);
        }
    }

    public MethodExecutor getExecutor(String name, String method) {
        return executors.get(buildKey(name, method));
    }

    private MethodKey buildKey(String className, String methodName) {
        return new MethodKey(className, methodName);
    }


    @AllArgsConstructor
    static class MethodKey {
        String className;
        String methodName;

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            MethodKey methodKey = (MethodKey) o;
            return Objects.equals(className, methodKey.className) &&
                    Objects.equals(methodName, methodKey.methodName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(className, methodName);
        }
    }
}
